#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <stdlib.h>
#include <time.h>
#include "robot.h"
#include <stdio.h>
#include <string.h>

/*space key*/
#define SPACE_KEY ' '

int WINDOW_ID; 
int Window_Width = 600;
int Window_Height = 600;

/* rotation angle for the pyramid. */
double pyrRot = 0.0f;

int eyex, eyey, eyez, lookx, looky, lookz;
int robx, roby, robz;
int totbuild;
int robotHeadNum;
int robDir = 180;
int camDist = 30;
int camHeight = 15;
int lookOS = 10; //look off set
int stride = 25;
int pause = 0;
//Character used to handle the direction the robot faces.
// the vector from 0 0 0 to 10 0 0 would be pointing north
char direction = 'N';

int atIntersection = 1;

//Structs used to draw world
struct color
{
  GLfloat r;
  GLfloat g;
  GLfloat b;
};

struct checkspots
{
	GLfloat spotx;
	GLfloat spotz;
};

struct building
{
  int buildingName;
  int drawBuilding;
  int buildingType;
  GLfloat x;
  GLfloat y;
  GLfloat z;
  GLfloat posx;
  GLfloat posz;
  struct color buildingColor;
};

struct building buildings[3600];
struct color colors[7];

static void PrintString(void *font, char *str)
{
   int i,len=strlen(str);

   for(i=0;i < len; i++)
      glutBitmapCharacter(font,*str++);
}

void CallBackResizeScene(int Width, int Height)
{
  /*Avoid Core Dump*/
  if (Height == 0)
    Height = 1;
  
  glViewport(0, 0, Width, Height);
  
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.05f,10000.0f);
   
  glMatrixMode(GL_MODELVIEW);
  
  Window_Width  = Width;
  Window_Height = Height;
}

int GenerateBuildings()
{
	int totalBuildings = -1;

	//initialize random number seed
	srand ( time(NULL) );
	
	int i,j,k;
	int startx, startz;
	startx = -1025;
	startz = 1025;
	//Loop through city blocks
	for (i = 0; i < 20; i++)
    {
		for (j = 0; j < 20; j++)
		{
			//Randomly generate number of buildings for this block
			int blocknum = rand() % 8 + 2;
			//Set Values for buildings
			struct checkspots spots[blocknum];
			for (k = 0; k < blocknum; k++)
			{
				//Set Values for new building
				totalBuildings ++;
				
				buildings[totalBuildings].buildingName = totalBuildings + 1 ;
				buildings[totalBuildings].drawBuilding = 1;
				buildings[totalBuildings].buildingType = rand() % 2;
				buildings[totalBuildings].x = (float) (rand() % 6 + 10);
				buildings[totalBuildings].y = (float) (rand() % 21 + 10);
				buildings[totalBuildings].z = (float) (rand() % 6 + 10);
				
				//Sets buildings spots and checks if they overlap with another buildings
				spots[k].spotx = rand() % 3 + 1;
				spots[k].spotz = rand() % 3 + 1;
				int isValidSpot = 1;
				do
				{
					isValidSpot = 1;
					int l;
					for (l = 0; l < k; l++)
					{
						if (spots[l].spotx == spots[k].spotx && spots[l].spotz == spots[k].spotz)
						{
							isValidSpot = 0;
							break;
						}
					}
					
					if (isValidSpot == 0)
					{
						spots[k].spotx = rand() % 3 + 1;
						spots[k].spotz = rand() % 3 + 1;
					}
				} while (isValidSpot == 0);
				
				//Assign final values for position and set color
				buildings[totalBuildings].posx = (float) (startx + (i * 100) + 20 + (spots[k].spotx * 30));
				buildings[totalBuildings].posz = (float) (startz - (j * 100) - 20 - (spots[k].spotz * 30));
				buildings[totalBuildings].buildingColor = colors[rand() % 3];	
				
			}
		}
	}
	totalBuildings++;
	
	return totalBuildings;
}

void SetupColors()
{
	//Red
	colors[0].r = 1.0;
	colors[0].g = 0.0; 
	colors[0].b = 0.0;
	
	//Green
	colors[1].r = 0.0;
	colors[1].g = 1.0; 
	colors[1].b = 0.0;
	
	//Blue
	colors[2].r = 0.0;
	colors[2].g = 0.0; 
	colors[2].b = 1.0;
	
	//Grey
	colors[3].r = 0.3;
	colors[3].g = 0.38; 
	colors[3].b = 0.38;
	
	//Ashpault
	colors[4].r = 0.2;
	colors[4].g = 0.2; 
	colors[4].b = 0.2;
	
	//white
	colors[5].r = 1.0;
	colors[5].g = 1.0; 
	colors[5].b = 1.0;
	
}

//Moved draw buildings to its own function to make select simplier
void DrawBuildings(int numberOfBuildings, GLenum mode)
{
	int i;
	
	for (i = 0; i < numberOfBuildings; i++)
    	{
		//Check if building is destroyed
		if (buildings[i].drawBuilding == 1)
		{	
			if (buildings[i].drawBuilding == 1)
			{	
				glPushMatrix();
				glColor4f(buildings[i].buildingColor.r, buildings[i].buildingColor.g, 
					buildings[i].buildingColor.b, 1.0);
				glTranslatef(buildings[i].posx, buildings[i].y/2 + 0.5 , buildings[i].posz);
				glScalef(buildings[i].x, buildings[i].y, buildings[i].z);
				
				if (buildings[i].buildingType == 0)
				{
					if (mode == GL_SELECT)
						glPushName(buildings[i].buildingName);
					glutSolidCube(1.0);
				}
				else if (buildings[i].buildingType == 1)
				{	
					if (mode == GL_SELECT)
						glPushName(buildings[i].buildingName);
					glBegin(GL_POLYGON);
						glVertex3f(0.5, -0.5, 0.5);
						glVertex3f(0.5, -0.5, -0.5);
						glVertex3f(-0.5, -0.5, -0.5);
						glVertex3f(-0.5, -0.5, 0.5);
					glEnd();
					if (mode == GL_SELECT)
						glPushName(buildings[i].buildingName);
					glBegin(GL_POLYGON);
						glVertex3f(0.0, 0.5, 0.0);
						glVertex3f(0.5, -0.5, -0.5);
						glVertex3f(-0.5, -0.5, -0.5);
					glEnd();
					if (mode == GL_SELECT)
						glPushName(buildings[i].buildingName);
					glBegin(GL_POLYGON);
						glVertex3f(0.0, 0.5, 0.0);
						glVertex3f(-0.5, -0.5, -0.5);
						glVertex3f(-0.5, -0.5, 0.5);
					glEnd();
					if (mode == GL_SELECT)
						glPushName(buildings[i].buildingName);
					glBegin(GL_POLYGON);
						glVertex3f(0.0, 0.5, 0.0);
						glVertex3f(-0.5, -0.5, 0.5);
						glVertex3f(0.5, -0.5, 0.5);
					glEnd();
					if (mode == GL_SELECT)
						glPushName(buildings[i].buildingName);
					glBegin(GL_POLYGON);
						glVertex3f(0.0, 0.5, 0.0);
						glVertex3f(0.5, -0.5, 0.5);
						glVertex3f(0.5, -0.5, -0.5);
					glEnd();
				}
				else
				{	
					//third shape not being drawn currently
					//glutSolidOctahedron();
				}
				
				glPopMatrix();			
			}
		}	
	}
}

void DrawWorld(int numberOfBuildings, GLenum mode)
{
	int i, j;
	int startx, startz;
	
	//Draw base
	glPushMatrix();
	glBegin(GL_POLYGON);
	glColor4f(colors[3].r, colors[3].g, colors[3].b, 1.0);
	glVertex3f(-1005.0, 0, 1005.0);
	glVertex3f(1005.0, 0, 1005.0);
	glVertex3f(1005.0, 0.0, -1005.0);
	glVertex3f(-1005.0, 0.0, -1005.0);
	glEnd();
	glPopMatrix();
		
	//Draw blocks
	startx = -995;
	startz = 995;
	for (i = 0; i < 20; i++)
	{
		for (j = 0; j < 20; j++)
		{
			for (j = 0; j < 20; j++)
			{
				glPushMatrix();
				glColor4f(colors[4].r, colors[4].g, colors[4].b, 1.0);
				glTranslatef((float) (startx + (i * 100) + 45), 0.5, (float) (startz - (j * 100) - 45));
				glScalef(90.0, 0.5, 90.0);
				glutSolidCube(1.0);
				glPopMatrix();
			}
		}
	}
	
  //Draw buildings
	DrawBuildings(numberOfBuildings, mode);
	
	//Draw lines
	
	startx = -1000;
	startz = 1000;
	
	for (i = 0; i < 21; i++)
	{
		//Draw Horizontal
		glPushAttrib(GL_ENABLE_BIT); 
		glColor4f(colors[5].r, colors[5].g, colors[5].b, 1.0);
		glEnable(GL_LINE_STIPPLE);
		glLineWidth(3);
		glLineStipple(15,0xAAAA);
		glBegin(GL_LINES);
			glVertex3f(1000 , 0.2, startz - (i * 100));
			glVertex3f(-1000 , 0.2, startz - (i * 100));
		glEnd();
		glPopAttrib();
		
		//Draw verticle
		glPushAttrib(GL_ENABLE_BIT); 
		glLineStipple(1, 0xAAAA);  
		glColor4f(colors[5].r, colors[5].g, colors[5].b, 1.0);
		glEnable(GL_LINE_STIPPLE);
		glLineWidth(3);
		glLineStipple(15,0xAAAA);
		glBegin(GL_LINES);
			glVertex3f(startx + (i * 100) , 0.2, 1000);
			glVertex3f(startx + (i * 100) , 0.2, -1000);
		glEnd();
		glPopAttrib();
	}
}



//calling this enables lighting!
void lights(){
   GLfloat white[] = {1,1,1};
   GLfloat direction[] = {0,1,0,0};
   
//Lighting stuff!
  glEnable(GL_LIGHTING);
  glEnable(GL_COLOR_MATERIAL);

  glLightfv(GL_LIGHT0, GL_DIFFUSE, white);
  glLightfv(GL_LIGHT0, GL_AMBIENT, white);
  glLightfv(GL_LIGHT0, GL_SPECULAR, white);
  glLightfv(GL_LIGHT0, GL_POSITION, direction);

  glEnable(GL_LIGHT0);
}
     

void init(int Width, int Height)	        
{
  //Setup Code
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);	  
  glClearDepth(1.0);				
  glDepthFunc(GL_LESS);			        
  glEnable(GL_DEPTH_TEST);		        
  glShadeModel(GL_SMOOTH);	
  SetupColors();
  totbuild = GenerateBuildings();
  
  glMatrixMode(GL_PROJECTION);

  //STUFF TOM ADDED:
  cylinder=gluNewQuadric();
  gluQuadricNormals(cylinder, GLU_SMOOTH);

  lights(); //!
  //END STUFF
  
  glLoadIdentity();				

  CallBackResizeScene(Width,Height);/*Returns us to model view*/
}

void DrawGLScene()
{
  char buf[80];

  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
 
  //limiting functions
  if(eyex < -1000 - camDist)
    eyex = -1000 - camDist; 
  if(eyex > 1000 + camDist)
    eyex = 1000 + camDist;
  if(eyez < -1000 - camDist)
    eyez = -1000 - camDist;
  if(eyez > 1000 + camDist)
    eyez = 1000 + camDist;

  if(robx < -1000)
    robx = -1000; 
  if(robx > 1000)
    robx = 1000;
  if(robz < -1000)
    robz = -1000;
  if(robz > 1000)
    robz = 1000;

  if(lookx < -1000)
    lookx = -1000; 
  if(lookx > 1000)
    lookx = 1000;
  if(lookz < -1000)
    lookz = -1000;
  if(lookz > 1000)
    lookz = 1000;
  

  glLoadIdentity();
  gluLookAt(eyex, eyey, eyez,   /* Eye */
	    lookx, looky + lookOS, lookz,   /* Look at */
  	    0.0, 1.0, 0.0);  /* Up vector */
  
  glPushMatrix();
  DrawWorld(totbuild, GL_RENDER);//Darryl's Draw World Function
  glPopMatrix();

  //Call to Tom's robot draw-er
  glPushMatrix();
  glTranslatef(robx, roby, robz);
  glRotatef(robDir, 0.0f, 1.0f, 0.0f);
  glColor3f(0.6, 0.6, 0.6);
  robot(robotHeadNum);
  glPopMatrix();

  //this cube is just being used right now to make the center of the city.
  glPushMatrix();
  glScalef(0.98, 0.98, 0.98);
  glColor3f(0.0f, 0.0f, 0.50f);
  glutSolidCube(1.0);
  glColor3f(1.0f, 1.0f, 1.0f);
  glutWireCube(1.0);
  glPopMatrix();
  
  // Move back to the origin
  glLoadIdentity();
  
  //set viewport for rendering the UI
  glViewport(0, 0, Window_Width, Window_Height);
  
  glMatrixMode(GL_PROJECTION);
  
  glPushMatrix();

  glLoadIdentity();
  glOrtho(0,Window_Width,0,Window_Height,-1.0,1.0);

  if(pause == 1){
    sprintf(buf, "Game Paused");
    glColor3f(1,0,0);
    glRasterPos2i(Window_Width/2 - 35.0, Window_Height/2 +20.0);
    PrintString(GLUT_BITMAP_HELVETICA_12, buf);
  }
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);

  if((robx%100 == 0)&&(robz%100 == 0))
    atIntersection = 1;
  else
    atIntersection = 0;

  glFlush();
  glutSwapBuffers();
  
}

void processHits(GLint hits, GLuint buffer[]){
	unsigned int i;
	for(i=0;i<totbuild;i++){
		buildings[i].drawBuilding=0;
	}

   /*printf ("hits = %d\n", hits);
   ptr = (GLuint *) buffer;
   for (i = 0; i < hits; i++) {	//  for each hit  
      names = *ptr;
      printf (" number of names for this hit = %d\n", names); ptr++;
      printf("  z1 is %g;", (float) *ptr/0x7fffffff); ptr++;
      printf(" z2 is %g\n", (float) *ptr/0x7fffffff); ptr++;
      printf ("   names are ");
	//buildings[*ptr].drawBuilding = 0;
      for (j = 0; j < names; j++) { //  for each name 
	buildings[*ptr].drawBuilding = 0;
         printf ("%d ", *ptr);
         ptr++;
      }
      printf ("\n");
   }
   printf ("\n");*/
}

/* my mouse function  */
void myMouse(int button, int state, int x, int y){
	switch(button){
		GLuint selectBuf[512];
		GLint hits;
		GLint viewport[4];
		case GLUT_LEFT_BUTTON:
			if(state == GLUT_DOWN){
				glInitNames();
				glPushName(0);				
				glSelectBuffer (512, selectBuf);

				glGetIntegerv (GL_VIEWPORT, viewport);
				glMatrixMode (GL_PROJECTION);

				glPushMatrix ();
				glLoadIdentity ();

				gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3] - y), 5.0, 5.0, viewport);
				//gluOrtho2D(-2.0, 2.0, -2.0, 2.0);
				gluPerspective(45.0f, (GLfloat) (viewport[2]-viewport[0])/(GLfloat) (viewport[3]-viewport[1]), 0.1f, 100.0f);
				
				glRenderMode(GL_SELECT);
				DrawWorld(totbuild, GL_SELECT);

				glPopMatrix ();
				glFlush ();

				hits = glRenderMode (GL_RENDER);
				if(hits>0)
					processHits(hits, selectBuf);
				glMatrixMode(GL_MODELVIEW);
				glutPostRedisplay();
			}
		break;
	}
}

void specialKeyPressed(int key, int x, int y){
  
   if(pause == 0)
      switch (key){
	 case GLUT_KEY_F1: 
	    robotHeadNum = 0;
	    break;
	 case GLUT_KEY_F2:
	    robotHeadNum--;
	    break;
	 case GLUT_KEY_F3:
	    robotHeadNum++;
	    break;
	 case GLUT_KEY_F4:
	    switch (direction){
	       case 'N':
		  eyex = robx - camDist;
		  eyez = robz;
		  break;
	       case 'W':
		  eyex = robx;
		  eyez = robz + camDist;
		  break;
	       case 'S':
		  eyex = robx + camDist;
		  eyez = robz;
		  break;
	       case 'E':
		  eyex = robx;
		  eyez = robz - camDist;
		  break;
	       default:
		  break;
	    }
	    eyey = camHeight;
	    break;
	 case GLUT_KEY_F5:
	    switch (direction){
	       case 'N':
		  eyez = robz - camDist;
		  eyex = robx - camDist;
		  break;
	       case 'E':
		  eyez = robz - camDist;
		  eyex = robx + camDist;
		  break;
	       case 'S':
		  eyez = robz + camDist;
		  eyex = robx + camDist;
		  break;
	       case 'W':
		  eyez = robz + camDist;
		  eyex = robx - camDist;
		  break;
	       default:
		  break;
	    }
	    eyey = looky + (2*lookOS);
	    break;
	 case GLUT_KEY_F6: 
	    switch (direction){
	       case 'N':
		  eyez = robz + camDist;
		  eyex = robx - camDist;
		  break;
	       case 'E':
		  eyez = robz - camDist;
		  eyex = robx - camDist;
		  break;
	       case 'S':
		  eyez = robz - camDist;
		  eyex = robx + camDist;
		  break;
	       case 'W':
		  eyez = robz + camDist;
		  eyex = robx + camDist;
		  break;
	       default:
		  break;
	    }
	    eyey = looky + (2*lookOS);
	    break;
	 case GLUT_KEY_F7:
	    switch (direction){
	       case 'N':
		  eyez = robz + camDist;
		  eyex = robx + camDist;
		  break;
	       case 'E':
		  eyez = robz + camDist;
		  eyex = robx - camDist;
		  break;
	       case 'S':
		  eyez = robz - camDist;
		  eyex = robx - camDist;
		  break;
	       case 'W':
		  eyez = robz - camDist;
		  eyex = robx + camDist;
		  break;
	       default:
		  break;
	    }
	    eyey = looky + (2*lookOS);
	    break;
	 case GLUT_KEY_F8:
	    switch (direction){
	       case 'N':
		  eyez = robz - camDist;
		  eyex = robx + camDist;
		  break;
	       case 'E':
		  eyez = robz + camDist;
		  eyex = robx + camDist;
		  break;
	       case 'S':
		  eyez = robz + camDist;
		  eyex = robx - camDist;
		  break;
	       case 'W':
		  eyez = robz - camDist;
		  eyex = robx - camDist;
		  break;
	       default:
		  break;
	    }
	    eyey = looky + (2*lookOS);
	    break;
      }
   
};

/* my key function */
void keyPressed(unsigned char key, int x, int y){
  
   if((key == 'p')||(key == 'P')){
      pause = pause > 0 ? 0 : 1;
   }
   
   if(pause == 0)
      switch (key){
	 case 'a': //right turn
	 case 'A':
	    if(atIntersection == 1)
	    {
	       robDir -= 90;
	       switch (direction){
		  case 'N': 
		     eyez = lookz - camDist;
		     eyex = lookx;
		     direction = 'E';
		     break;
		  case 'E': 
		     eyez = lookz;
		     eyex = lookx + camDist;
		     direction = 'S';
		     break;
		  case 'S': 
		     eyez = lookz + camDist;
		     eyex = lookx;
		     direction = 'W';
		     break;
		  case 'W': 
		     eyez = lookz;
		     eyex = lookx - camDist;
		     direction = 'N';
		     break;
		  default:
		     break;
	       }
	    }
	    break;
	 case 'q':
	 case 'Q'://left turn
	    if(atIntersection == 1)
	    {
	       robDir += 90;
	       switch (direction){
		  case 'N': 
		     eyez = lookz + camDist;
		     eyex = lookx;
		     direction = 'W';
		     break;
		  case 'W': 
		     eyez = lookz;
		     eyex = lookx + camDist;
		     direction = 'S';
		     break;
		  case 'S': 
		     eyez = lookz - camDist;
		     eyex = lookx;
		     direction = 'E';
		     break;
		  case 'E': 
		     eyez = lookz;
		     eyex = lookx - camDist;
		     direction = 'N';
		     break;
		  default:
		     break;
	       }
	    }
	    break;
	 case 'z':
	 case 'Z': //move forward
	    switch (direction){
	       case 'N':
		  eyex += stride;
		  lookx += stride;
		  robx += stride;
		  break;
	       case 'E':
		  eyez += stride;
		  lookz += stride;
		  robz += stride;
		  break;
	       case 'S':
		  eyex -= stride;
		  lookx -= stride;
		  robx -= stride;
		  break;
	       case 'W':
		  eyez -= stride;
		  lookz -= stride;
		  robz -= stride;
		  break;
	       default:
		  break;
	    }
	    break;
	 case 'r':
	 case 'R':
	    eyex = 0 - camDist; 
	    eyey = 0 + camHeight;
	    eyez = 0; 
	    lookx = 0; 
	    looky = 0;
	    lookz = 0;
	    robx = 0;
	    roby = 0;
	    robz = 0;
	    break;

//tom's temporary camera controls
	 case 'n':
	    eyez--;
	    break;
	 case 'm':
	    eyez++;
	    break;
	 case 'j':
	    eyey--;
	    break;
	 case 'k':
	    eyey++;
	    break;
	 case 'i':
	    eyex--;
	    break;
	 case 'o':
	    eyex++;
	    break;
//end temporary camera controls
	   
	 default: 
	    break;
      }
} 

void spin()
{
  spinner+=0.3;
   
  if (spinner >= 360)
    spinner -= 360; 
   
  glutPostRedisplay();  
}

int main(int argc, char **argv)
{  
  //beginning values
  eyex-= camDist; 
  eyey = camHeight;
  eyez = 0; 
  lookx = 0; 
  looky = 0;
  lookz = 0;
  robx = 0;
  roby = 0;
  robz = 0;
 
  glutInit(&argc, argv);  

  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);  

  glutInitWindowSize(600, 600);  
  glutInitWindowPosition(0, 0);
  WINDOW_ID = glutCreateWindow("PROJECT");
  glutDisplayFunc(&DrawGLScene);  
  //glutIdleFunc(&DrawGLScene);

  glutIdleFunc(&spin); //TOM added this, needed to spin the antenna
  glutReshapeFunc(&CallBackResizeScene);
  glutMouseFunc(&myMouse);
  glutKeyboardFunc(&keyPressed);
  glutSpecialFunc(&specialKeyPressed);
  init(600, 600);
  
  glutMainLoop();  

  return 1;
}




